package jee.boot.encrypt.aes;


import jee.boot.common.utils.JsonMapper;
import jee.boot.encrypt.annotation.EncryptDecryptClass;
import jee.boot.encrypt.annotation.EncryptDecryptField;
import jee.boot.encrypt.config.DataEncryptConfig;
import jee.boot.encrypt.config.IEncryptDecrypt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * 采用AES对数据进行加解密
 */
public class AesImpl implements IEncryptDecrypt {
    private Logger logger = LoggerFactory.getLogger(getClass());
    private DataEncryptConfig config;

    public AesImpl(DataEncryptConfig config) {
        this.config = config;
    }

    /**
     * 加密方法
     *
     * @param entity 被加密对象
     * @return
     */
    @Override
    public <T> T encrypt(T entity) {
        if (entity == null) {
            return null;
        }
        try {
            Class<?> entityClass = entity.getClass();
            if (needToDecrypt(entity)) {
                //获取所有的字段(包含父类)
                Class clazz = entityClass;
                List<Field> fields = new ArrayList<>();
                while (clazz != null) {
                    fields.addAll(new ArrayList<Field>(Arrays.asList(clazz.getDeclaredFields())));
                    clazz = clazz.getSuperclass();
                }
                for (Field field : fields) {
                    // 跳过静态属性
                    String mod = Modifier.toString(field.getModifiers());
                    if (mod.indexOf("static") != -1) {
                        continue;
                    }
                    field.setAccessible(true);
                    Object property = field.get(entity);
                    if (Objects.nonNull(property)) {
                        Class<?> fieldType = field.getType();
                        EncryptDecryptClass fieldEncryptDecryptClass = AnnotationUtils.findAnnotation(fieldType, EncryptDecryptClass.class);
                        if (Objects.nonNull(fieldEncryptDecryptClass)) {
                            // 递归遍历对象类型的数据,如果需要被加密
                            Object encrypt = encrypt(property);
                            field.set(entity, encrypt);
                        }
                        EncryptDecryptField encryptDecryptField = field.getAnnotation(EncryptDecryptField.class);
                        // 属性需要被加密
                        if (Objects.nonNull(encryptDecryptField)) {
                            field.set(entity, AesEncryptUtils.encrypt(property.toString(), config.getAesKey()));
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return entity;
    }

    /**
     * 解密方法
     *
     * @param entity 被解密对象
     * @return
     */
    @Override
    public <T> T decrypt(T entity) {
        if (entity == null) {
            return null;
        }
        try {
            Class<?> entityClass = entity.getClass();
            if (needToDecrypt(entity)) {
                //获取所有的字段(包含父类)
                Class clazz = entityClass;
                List<Field> fields = new ArrayList<>();
                while (clazz != null) {
                    fields.addAll(new ArrayList<Field>(Arrays.asList(clazz.getDeclaredFields())));
                    clazz = clazz.getSuperclass();
                }
                for (Field field : fields) {
                    // 跳过静态属性
                    String mod = Modifier.toString(field.getModifiers());
                    if (mod.indexOf("static") != -1) {
                        continue;
                    }
                    field.setAccessible(true);
                    Object property = field.get(entity);
                    if (Objects.nonNull(property)) {
                        Class<?> fieldType = field.getType();
                        EncryptDecryptClass fieldEncryptDecryptClass = AnnotationUtils.findAnnotation(fieldType, EncryptDecryptClass.class);
                        if (Objects.nonNull(fieldEncryptDecryptClass)) {
                            // 递归遍历对象类型的数据,如果需要被解密
                            Object decrypt = decrypt(property);
                            field.set(entity, decrypt);

                        }
                        EncryptDecryptField encryptDecryptField = field.getAnnotation(EncryptDecryptField.class);
                        // 属性需要被解密
                        if (Objects.nonNull(encryptDecryptField)) {
                            field.set(entity, AesEncryptUtils.decrypt(property.toString(), config.getAesKey()));
                        }

                    }
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return entity;
    }

    /**
     * 判断当前对象是否需要加解密
     *
     * @param object 数据对象
     * @return
     */
    @Override
    public boolean needToDecrypt(Object object) {
        Class<?> objectClass = object.getClass();
        EncryptDecryptClass encryptDecryptClass = AnnotationUtils.findAnnotation(objectClass, EncryptDecryptClass.class);
        if (Objects.nonNull(encryptDecryptClass)) {
            return true;
        }
        return false;
    }

    public static void main(String[] args) {
        DataEncryptConfig config = new DataEncryptConfig();
        config.setAesKey("1234567890123456");
        AesImpl aes = new AesImpl(config);
        Demo demo = new Demo();
        demo.setCode("demo-code");
        demo.setName("demo-name");
        Demo2 demo2 = new Demo2();
        demo2.setMail("demo2-mail");
        demo2.setPhone("demo2-phone");
        demo.setDemo(demo2);
        System.out.println("加密前:" + JsonMapper.toJsonString(demo));
        Demo encrypt = aes.encrypt(demo);
        System.out.println("加密后:" + JsonMapper.toJsonString(demo));
        Demo decrypt = aes.decrypt(demo);
        System.out.println("解密后:" + JsonMapper.toJsonString(demo));


    }

}

@EncryptDecryptClass
class Demo {
    @EncryptDecryptField
    private String name;
    private String code;
    private Demo2 demo;

    public Demo() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public Demo2 getDemo() {
        return demo;
    }

    public void setDemo(Demo2 demo) {
        this.demo = demo;
    }
}

@EncryptDecryptClass
class Demo2 {
    @EncryptDecryptField
    private String mail;
    private String phone;

    public Demo2() {
    }

    public String getMail() {
        return mail;
    }

    public void setMail(String mail) {
        this.mail = mail;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }
}
